
#include "tolua_fix.h"
#include <stdlib.h>


static int s_function_ref_id = 0;

TOLUA_API void toluafix_open(lua_State* L)
{
	lua_pushstring(L, TOLUA_REFID_PTR_MAPPING);
	lua_newtable(L);
	lua_rawset(L, LUA_REGISTRYINDEX);

	lua_pushstring(L, TOLUA_REFID_TYPE_MAPPING);
	lua_newtable(L);
	lua_rawset(L, LUA_REGISTRYINDEX);

	lua_pushstring(L, TOLUA_REFID_FUNCTION_MAPPING);
	lua_newtable(L);
	lua_rawset(L, LUA_REGISTRYINDEX);
}

#if 0
TOLUA_API int toluafix_pushusertype_ccobject(lua_State* L,
	int refid,
	int* p_refid,
	void* ptr,
	const char* type)
{
	if (ptr == NULL || p_refid == NULL)
	{
		lua_pushnil(L);
		return -1;
	}

	Ref* vPtr = static_cast<Ref*>(ptr);
	const char* vType = getLuaTypeName(vPtr, type);

	if (*p_refid == 0)
	{
		*p_refid = refid;

		lua_pushstring(L, TOLUA_REFID_PTR_MAPPING);
		lua_rawget(L, LUA_REGISTRYINDEX);                           /* stack: refid_ptr */
		lua_pushinteger(L, refid);                                  /* stack: refid_ptr refid */
		lua_pushlightuserdata(L, vPtr);                              /* stack: refid_ptr refid ptr */

		lua_rawset(L, -3);                  /* refid_ptr[refid] = ptr, stack: refid_ptr */
		lua_pop(L, 1);                                              /* stack: - */

		lua_pushstring(L, TOLUA_REFID_TYPE_MAPPING);
		lua_rawget(L, LUA_REGISTRYINDEX);                           /* stack: refid_type */
		lua_pushinteger(L, refid);                                  /* stack: refid_type refid */
		lua_pushstring(L, vType);                                    /* stack: refid_type refid type */
		lua_rawset(L, -3);                /* refid_type[refid] = type, stack: refid_type */
		lua_pop(L, 1);                                              /* stack: - */

		//printf("[LUA] push CCObject OK - refid: %d, ptr: %x, type: %s\n", *p_refid, (int)ptr, type);
	}

	tolua_pushusertype_and_addtoroot(L, vPtr, vType);

	return 0;
}


TOLUA_API int toluafix_remove_ccobject_by_refid(lua_State* L, int refid)
{
	void* ptr = NULL;
	const char* type = NULL;
	void** ud = NULL;
	if (refid == 0) return -1;

	// get ptr from tolua_refid_ptr_mapping
	lua_pushstring(L, TOLUA_REFID_PTR_MAPPING);
	lua_rawget(L, LUA_REGISTRYINDEX);                               /* stack: refid_ptr */
	lua_pushinteger(L, refid);                                      /* stack: refid_ptr refid */
	lua_rawget(L, -2);                                              /* stack: refid_ptr ptr */
	ptr = lua_touserdata(L, -1);
	lua_pop(L, 1);                                                  /* stack: refid_ptr */
	if (ptr == NULL)
	{
		lua_pop(L, 1);
		// Lua stack has closed, C++ object not in Lua.
		// printf("[LUA ERROR] remove CCObject with NULL ptr, refid: %d\n", refid);
		return -2;
	}

	// remove ptr from tolua_refid_ptr_mapping
	lua_pushinteger(L, refid);                                      /* stack: refid_ptr refid */
	lua_pushnil(L);                                                 /* stack: refid_ptr refid nil */
	lua_rawset(L, -3);                     /* delete refid_ptr[refid], stack: refid_ptr */
	lua_pop(L, 1);                                                  /* stack: - */


	// get type from tolua_refid_type_mapping
	lua_pushstring(L, TOLUA_REFID_TYPE_MAPPING);
	lua_rawget(L, LUA_REGISTRYINDEX);                               /* stack: refid_type */
	lua_pushinteger(L, refid);                                      /* stack: refid_type refid */
	lua_rawget(L, -2);                                              /* stack: refid_type type */
	if (lua_isnil(L, -1))
	{
		lua_pop(L, 2);
		printf("[LUA ERROR] remove CCObject with NULL type, refid: %d, ptr: %p\n", refid, ptr);
		return -1;
	}

	type = lua_tostring(L, -1);
	lua_pop(L, 1);                                                  /* stack: refid_type */

	// remove type from tolua_refid_type_mapping
	lua_pushinteger(L, refid);                                      /* stack: refid_type refid */
	lua_pushnil(L);                                                 /* stack: refid_type refid nil */
	lua_rawset(L, -3);                    /* delete refid_type[refid], stack: refid_type */
	lua_pop(L, 1);                                                  /* stack: - */

	// get ubox
	luaL_getmetatable(L, type);                                     /* stack: mt */
	lua_pushstring(L, "tolua_ubox");                                /* stack: mt key */
	lua_rawget(L, -2);                                              /* stack: mt ubox */
	if (lua_isnil(L, -1))
	{
		// use global ubox
		lua_pop(L, 1);                                              /* stack: mt */
		lua_pushstring(L, "tolua_ubox");                            /* stack: mt key */
		lua_rawget(L, LUA_REGISTRYINDEX);                           /* stack: mt ubox */
	};


	// cleanup root
	tolua_remove_value_from_root(L, ptr);

	lua_pushlightuserdata(L, ptr);                                  /* stack: mt ubox ptr */
	lua_rawget(L, -2);                                               /* stack: mt ubox ud */
	if (lua_isnil(L, -1))
	{
		// Lua object has released (GC), C++ object not in ubox.
		//printf("[LUA ERROR] remove CCObject with NULL ubox, refid: %d, ptr: %x, type: %s\n", refid, (int)ptr, type);
		lua_pop(L, 3);
		return -3;
	}

	// cleanup peertable
	lua_pushvalue(L, LUA_REGISTRYINDEX);
	lua_setfenv(L, -2);

	ud = (void**)lua_touserdata(L, -1);
	lua_pop(L, 1);                                                  /* stack: mt ubox */
	if (ud == NULL)
	{
		printf("[LUA ERROR] remove CCObject with NULL userdata, refid: %d, ptr: %p, type: %s\n", refid, ptr, type);
		lua_pop(L, 2);
		return -1;
	}

	// clean userdata
	*ud = NULL;

	lua_pushlightuserdata(L, ptr);                                  /* stack: mt ubox ptr */
	lua_pushnil(L);                                                 /* stack: mt ubox ptr nil */
	lua_rawset(L, -3);                             /* ubox[ptr] = nil, stack: mt ubox */

	lua_pop(L, 2);
	//printf("[LUA] remove CCObject, refid: %d, ptr: %x, type: %s\n", refid, (int)ptr, type);
	return 0;
}
#endif

TOLUA_API int toluafix_ref_function(lua_State* L, int lo, int def)
{
	// function at lo
	if (!lua_isfunction(L, lo)) return 0;

	s_function_ref_id++;

	lua_pushstring(L, TOLUA_REFID_FUNCTION_MAPPING);
	lua_rawget(L, LUA_REGISTRYINDEX);                           /* stack: fun ... refid_fun */
	lua_pushinteger(L, s_function_ref_id);                      /* stack: fun ... refid_fun refid */
	lua_pushvalue(L, lo);                                       /* stack: fun ... refid_fun refid fun */

	lua_rawset(L, -3);                  /* refid_fun[refid] = fun, stack: fun ... refid_ptr */
	lua_pop(L, 1);                                              /* stack: fun ... */

	return s_function_ref_id;

	// lua_pushvalue(L, lo);                                           /* stack: ... func */
	// return luaL_ref(L, LUA_REGISTRYINDEX);
}

TOLUA_API void toluafix_get_function_by_refid(lua_State* L, int refid)
{
	lua_pushstring(L, TOLUA_REFID_FUNCTION_MAPPING);
	lua_rawget(L, LUA_REGISTRYINDEX);                           /* stack: ... refid_fun */
	lua_pushinteger(L, refid);                                  /* stack: ... refid_fun refid */
	lua_rawget(L, -2);                                          /* stack: ... refid_fun fun */
	lua_remove(L, -2);                                          /* stack: ... fun */
}

TOLUA_API void toluafix_remove_function_by_refid(lua_State* L, int refid)
{
	lua_pushstring(L, TOLUA_REFID_FUNCTION_MAPPING);
	lua_rawget(L, LUA_REGISTRYINDEX);                           /* stack: ... refid_fun */
	lua_pushinteger(L, refid);                                  /* stack: ... refid_fun refid */
	lua_pushnil(L);                                             /* stack: ... refid_fun refid nil */
	lua_rawset(L, -3);                  /* refid_fun[refid] = fun, stack: ... refid_ptr */
	lua_pop(L, 1);                                              /* stack: ... */

	// luaL_unref(L, LUA_REGISTRYINDEX, refid);
}

// check lua value is funciton
TOLUA_API int toluafix_isfunction(lua_State* L, int lo, const char* type, int def, tolua_Error* err)
{
	if (lua_gettop(L) >= abs(lo) && lua_isfunction(L, lo))
	{
		return 1;
	}
	err->index = lo;
	err->array = 0;
	err->type = "[not function]";
	return 0;
}

TOLUA_API int toluafix_totable(lua_State* L, int lo, int def)
{
	return lo;
}

TOLUA_API int toluafix_istable(lua_State* L, int lo, const char* type, int def, tolua_Error* err)
{
	return tolua_istable(L, lo, def, err);
}

TOLUA_API void toluafix_stack_dump(lua_State* L, const char* label)
{
	int i;
	int top = lua_gettop(L);
	printf("Total [%d] in lua stack: %s\n", top, label != 0 ? label : "");
	for (i = -1; i >= -top; i--)
	{
		int t = lua_type(L, i);
		switch (t)
		{
		case LUA_TSTRING:
			printf("  [%02d] string %s\n", i, lua_tostring(L, i));
			break;
		case LUA_TBOOLEAN:
			printf("  [%02d] boolean %s\n", i, lua_toboolean(L, i) ? "true" : "false");
			break;
		case LUA_TNUMBER:
			printf("  [%02d] number %g\n", i, lua_tonumber(L, i));
			break;
		default:
			printf("  [%02d] %s\n", i, lua_typename(L, t));
		}
	}
	printf("\n");
}
